package org.tokaf.algorithm;

import java.util.ArrayList;
import java.util.Iterator;

import org.tokaf.TopKElement;
import org.tokaf.bestcolumnfinder.BestColumnFinder;
import org.tokaf.datasearcher.DataSearcher;
import org.tokaf.rater.Rater;

/**
 * <p>Fagins algorithm has three phases - first, descent until we have k object
 * with all ratings filled. In the second, we search rating of all remainng
 * objects, that lack some rating. In the thrid, we compute the overall rating
 * and sort the list. </p> <p>Copyright (c) 2006</p> <p>Company: </p>
 * @author Alan Eckhardt
 * @version 1.0
 */

public class FaginsAlgorithm extends Algorithm {
	private int phase = -1;

	int numberTotal = -1;

	public FaginsAlgorithm(DataSearcher[] data, Rater rater,
			BestColumnFinder finder, String prefix, int K) {
		super(data, rater, finder, prefix, K);
	}

	/**
	 * This algorithm doesn't have threshold value.
	 */
	public TopKElement getThreshold() {
		return null;
	}

	/**
	 * run
	 * @return QueryResultsTable
	 */
	public ArrayList run() {
		long startTime = System.nanoTime();
		if (data.length > 0) {
			firstPhase();
			secondPhase();
			thirdPhase();
		}
		runTime = System.nanoTime() - startTime;
		return createArrayList();
	}

	// Hleda, dokud u K objektu ma vsechna hodnoceni
	void firstPhase() {
		boolean end = false;
		numberTotal = 0;
		// Dokud nemam K plne ohodnocenych objektu
		while (end != true && numberTotal < K) {
			// Pro vsechny seznamy vezmu zaznam uplne nahore
			for (int i = 0; i < data.length; i++) {
				if (finished[i] == true)
					continue;
				else if (!data[i].hasNext()) {
					finished[i] = true;
					end = checkEnd();
					continue;
				}

				long startTime = System.nanoTime();
				Object subject = data[i].getField(0);
				// Object predicate = data[i].getField(1);
				Object object = data[i].getField(2);
				dataTime = dataTime + System.nanoTime() - startTime;
				TopKElement newSubject = new TopKElement(subject, -1,
						data.length);
				// Hledam, jestli tento objekt uz nekde nemam

				startTime = System.nanoTime();
				// Je to novy objekt, musim ho vlozit do seznamu
				TopKElement temp = findEntity(newSubject);
				searchTime = searchTime + System.nanoTime() - startTime;
				if (temp == null) {
					topKlist.put(newSubject.name, newSubject);

					newSubject.setFound(i, step);
					newSubject.setPosition(i, data[i].getPosistion());

				} else
					newSubject = temp;

				// Pridam aktualni rating k zaznamu
				// rater.AddRating(newSubject, predicate.toString(), object);
				newSubject.setRating(i, object, data[i]);
				newSubject.setFound(i, data[i].getPosistion());
				boolean hasNull = false;
				for (int j = 0; j < newSubject.getLength(); j++) {
					if (newSubject.isNull(j)) {
						hasNull = true;
						break;
					}
				}
				if (!hasNull)
					numberTotal++;
				// Jsem na konci seznamu, dojede se cyklus pres dataSearchery
				// a vnejsi cyklus konci.
				startTime = System.nanoTime();
				if (data[i].advance() == -1) {
					// Zrusim aktualni dataset, uz ho nepotrebuji, dosel jsem na
					// end
					finished[i] = true;
					// Zjistim, jestli neskoncili uz vsichni
					end = checkEnd();
				}
				dataTime = dataTime + System.nanoTime() - startTime;

				step++;
				if (maxStep != -1 && maxStep >= step)
					break;
			}
		}

	}

	void firstStep() {
		if (end)
			return;
		actColumn = finder.findColumn(this);
		if (actColumn == -1) {
			end = true;
			return;
		}

		Object subject = data[actColumn].getField(0);
		Object object = data[actColumn].getField(2);
		TopKElement newSubject = new TopKElement(subject, -1, data.length);
		// Hledam, jestli tento objekt uz nekde nemam
		// int indObject = Arrays.binarySearch(topKlist.toArray(), novy, new
		// MyTopKCompareNames());

		// Je to novy objekt, musim ho vlozit do seznamu
		TopKElement temp = findEntity(newSubject);
		if (temp == null) {
			topKlist.put(newSubject.name, newSubject);
		} else
			newSubject = temp;

		// Pridam aktualni rating k zaznamu
		newSubject.setRating(actColumn, object, data[actColumn]);

		// Jsem na konci seznamu, dojede se cyklus pres dataSearchery
		// a vnejsi cyklus konci.
		if (data[actColumn].advance() == -1) {
			// Zrusim aktualni dataset, uz ho nepotrebuji, dosel jsem na end
			finished[actColumn] = true;
			// Zjistim, jestli neskoncili uz vsichni
			end = checkEnd();
		}
	}

	// Dohledam ohodnoceni vsech dosud nalezenych zaznamu
	void secondPhase() {
		Iterator i = topKlist.values().iterator();
		while (i.hasNext()) {
			TopKElement el = (TopKElement) i.next();
			for (int j = 0; j < el.getLength(); j++) {
				// Rating is missing, we will search for it in the list
				if (el.isNull(j)) {
					long startTime = System.nanoTime();
					Object object = data[j].findObject(el.name, 0, 2);
					dataTime = dataTime + System.nanoTime() - startTime;
					if (object == null)
						continue;
					el.setRating(j, object, data[j]);
				}
			}
		}
	}

	// Ohodnotim vsechny zaznamy v seznamu a setridim je podle rating
	void thirdPhase() {
		Iterator i = topKlist.values().iterator();
		while (i.hasNext()) {
			long startTime = System.nanoTime();
			TopKElement el = (TopKElement) i.next();
			rater.rate(el, data);
			ratingTime = ratingTime + System.nanoTime() - startTime;
		}
	}

	public int step() {
		// TODO Auto-generated method stub
		if (end)
			return -1;
		switch (phase) {
		case -1:
			phase = 1;
			break;
		case 1:
			firstStep();
			actColumn++;
			if (end) {
				actColumn = 0;
				end = false;
				phase = 2;
			}
			break;
		case 2:
			secondPhase();
			thirdPhase();
			end = true;
			phase = 3;
			return -1;
		case 3:
			return -1;
		}
		return actColumn - 1;
	}

}
